__author__ = "Abhinav Jha"
__credits__ = ["Abhinav Jha"]
__version__ = "0.1"
__maintainer__ = "Abhinav Jha"
__email__ = "abhinav@knowlarity.com"
__status__ = "Development"

from datetime import datetime
import getopt
import inspect
import new
import os
import random
import re
import string
import sys
import urllib, urllib2

#===========================================
# A generic Downloader class, so we can use
# it for shit other than YouTube.
#===========================================
class Downloader(object):
    def __init__(self, *args, **kwargs):
        self.type = kwargs.get("type", None)
        self.url = kwargs.get("url", None)
        self.related = kwargs.get("related", None)
        self.playlist = kwargs.get("playlist", None)
        self.channel = kwargs.get("channel", None)
        self.pages = int(kwargs.get("pages", "0"))
        self.downloader_class = globals().get(self.type)
        self.downloader = self.downloader_class(related=self.related,
                                                playlist=self.playlist,
                                                channel=self.channel,
                                                url=self.url,
                                                pages=self.pages)

        self.valid = inspect.isclass(self.downloader_class)

    def download(self):
        if self.valid:
            self.downloader.download()

#=============================================
# A Generic YouTube Object from which other
# YouTube objects like PlayList and Channel
# derive.
#=============================================
class YouTubeObject(object):
    def __init__(self, *args, **kwargs):
        self.url = kwargs.get("url", None)

    def extract_links(self):
        matched_lines = [self.re.match(line.strip())
                            for line in urllib2.urlopen(self.url)
                            if self.re.match(line.strip())]

        video_ids = ["http://www.youtube.com/watch?v=%s" % (mo.group("watch_id")) for mo in matched_lines]
        video_ids_unique = set()

        for video_id in video_ids:
            video_ids_unique.add(video_id)

        return video_ids_unique


    def get_video_url(self):
        page_source = urllib2.urlopen(self.url)
        print self.url
        video_mo = self.re.match(self.url)
        video_id = video_mo.group("watch_id") if video_mo else ""
        arg_line = []
        title_line = []

        # Really, really hacky stuff going on here. 
        # Clean up !
        for line in page_source:
            x = arg_line.append(line.strip()) if 'swfHTML' in line.strip() else 0
            x = title_line.append(line.strip()) if 'document.title' in line.strip() else 0
        name_of_video = title_line[0].replace("document.title = ", "").\
                replace("{", "").replace("}", "").\
                replace("'", "").replace(";", "")
        name_of_video += ".flv"

        video_id_re = re.compile(r'^(?P<prefix>.*\&t\=)(?P<video_id>.*\%3D)(?P<suffix>\&.*)$')
        virm = video_id_re.match(arg_line[0])
        if virm :
            t = virm.group("video_id")
        video_name = name_of_video.replace("/", "")


        video_name = swfArgs["keywords"] if not video_name else video_name
        video_url = "video_id=%s&t=%s&fmt=18&el=detailpage&ps="
        video_url = video_url % (video_id, t)
        video_url = "http://www.youtube.com/get_video?" + video_url
        self.video_url = video_url
        self.video_name = video_name

    def reporthook(self, count, block_size, total_size):
        try:
            percent =  int(count*block_size*100/total_size)
        except:
            percent = -1
        sys.stdout.write("\r%2d%%" % (percent))
        sys.stdout.flush()

#=============================================
# A YouTube Channel. Not implemented yet.
#=============================================
class YouTubeChannel(YouTubeObject):
    def __init__(self, *args, **kwargs):
        super(YouTubeChannel, self).__init__(*args, **kwargs)
        self.re = None

    def download(self, *args, **kwargs):
        raise NotImplementedError

#==============================================
# Class representing a YouTube video.
#==============================================
class YouTubeVideo(YouTubeObject):
    def __init__(self, *args, **kwargs):
        super(YouTubeVideo, self).__init__(*args, **kwargs)
        self.orig_url = kwargs.get("url", None)
        self.url = self.orig_url
        self.links = [self.orig_url]
        self.amassed_links = []
        self.video_url = None
        self.video_name = None
        self.related = kwargs.get("related", False)
        self.re = re.compile(r"""(?xs)
                                ^(?P<prefix>.*\/watch\?v\=)
                                (?P<watch_id>.*)
                                (?P<suffix>&feature.*)*$
                                """)
        self.MAX_AMASSMENT = kwargs.get("max_amassment", 50)


    def download(self):
        if self.related:
            self.download_multiple()
        else:
            try:
                self.get_video_url()
                print "Getting %s, please wait." % (self.video_name)
                print self.video_url
                urllib.urlretrieve(self.video_url, self.video_name,
                                reporthook=self.reporthook)
            except (IOError, ValueError, TypeError, IndexError):
                print "Failure."
                import traceback
                import sys
                print sys.exc_info()
            except KeyboardInterrupt:
                print "Skipping."

    def download_multiple(self):
        for link in self.links:
            self.url = link
            self.amassed_links.extend(self.extract_links())
            if len(self.amassed_links) >= self.MAX_AMASSMENT:
                break

        self.related = False
        for link in self.amassed_links:
            self.url = link
            self.download()

#===========================================
# Class representing a YouTube playlist.
#===========================================
class YouTubePlaylist(YouTubeObject):
    def __init__(self, *args, **kwargs):
        super(YouTubePlaylist, self).__init__(*args, **kwargs)
        self.re = re.compile(r"""(?xs)
                                 ^(?P<prefix>.*\/watch\?v\=)
                                 (?P<watch_id>.*)
                                 (?P<suffix>&feature\=PlayList.*)$
                                 """)
        self.orig_url = kwargs.get("url", None)
        self.links = set()
        self.pages = kwargs.get("pages", 0)
        print "Forming List."
        for page in xrange(1, self.pages+1):
            print "Page %d..." % (int(page))
            self.url = self.orig_url + "&page=%d" % (int(page))
            orig_length = len(self.links)
            _ = [self.links.add(x) for x in self.extract_links()]
            print "Done - Added %d links." % (len(self.links) - orig_length)

        print "Formed list of %d videos." % (len(self.links))
        print self.links
        self.videos = [YouTubeVideo(url=link) for link in self.links]

    def download(self):
        for video in self.videos:
            video.download()

#================================================
# Class representing a YouTube downloader.
#================================================
class YouTubeDownloader(object):
    def __init__(self, related=False, channel=False,
                 playlist=False, url="", *args, **kwargs):

        self.related = related
        self.playlist = playlist
        self.channel = channel
        self.links = [url]
        if self.playlist:
            self.yt_obj = YouTubePlaylist(url=self.links[0], *args, **kwargs)
        elif self.channel:
            self.yt_obj = YouTubeChannel(url=self.links[0], *args, **kwargs)
        else:
            self.yt_obj = YouTubeVideo(url=self.links[0], *args, **kwargs)
            if self.related:
                self.yt_obj.related = True

    def download(self):
            self.yt_obj.download()



def show_usage():
    import os
    print "Wrong."
    os._exit(0)



if __name__ == "__main__":
    _supported_sites = ('YouTube',)
    l = getopt.getopt(sys.argv[1:], "s:v:rn:pcm:")
    opts = [x[0] for x in l[0]]
    args = [x[1] for x in l[0]]
    if '-v' not in opts:
        show_usage()
    elif '-p' in opts and '-n' not in opts:
        show_usage()
    else:
        site = args[opts.index('-s')] if '-s' in opts else 'YouTube'
        if site not in _supported_sites:
            show_usage()
        else:
            site = site.strip() + "Downloader"
            url = args[opts.index('-v')]
            playlist = '-p' in opts
            channel = '-c' in opts
            related = '-r' in opts
            pages = int(args[opts.index('-n')]) if '-n' in opts else 10
            max_amassment = int(args[opts.index('-m')]) if '-m' in opts else 50

        dl = Downloader(channel=channel,
                        max_amassment=max_amassment,
                        pages=pages,
                        playlist=playlist,
                        related=related,
                        type=site,
                        url=url,)
        dl.download()
